/*
    exec.c
    Copyright (C) 2011  Jiabo <jiabo2011@gmail.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "tb.h"
#include "../debug.h"
#include "../cpu.h"
#include "../global.h"
#include "../trans/translate.h"

#define CODE_GEN_ALIGN 4

int debug_stop = 0;

static const char *cc_op_str[] = {
	"NONE",
	"EFLAGS",
	"NONE",
	"NONE",
	
	"LOGICB",
	"LOGICW",
	"LOGICL",
	"NONE",

	"ADDB",
	"ADDW",
	"ADDL",
	"NONE",

	"CMPB",
	"CMPW",
	"CMPL",
	"NONE",

	"SUBB",
	"SUBW",
	"SUBL",
	"NONE",

	"INCB",
	"INCW",
	"INCL",
	"NONE",

	"DECB",
	"DECW",
	"DECL",
	"NONE",

	"SHLB",
	"SHLW",
	"SHLL",
	"NONE",

	"SHRB",
	"SHRW",
	"SHRL",
	"NONE",

	"MULB",
	"MULW",
	"MULL",
	"NONE",

	"ADCB",
	"ADCW",
	"ADCL",
	"NONE",

	"SBBB",
	"SBBW",
	"SBBL",
	"NONE",
};

static uint32_t saved_opc = 0, saved_op1, saved_op2;
static void run_arm_code(CPUX86State *cpu, uint32_t *code) __attribute__ ((optimize(0)));
static void run_arm_code(CPUX86State *cpu, uint32_t *code)
{
#if defined(__arm__)
	register uint32_t env asm("r4");
	register uint32_t memory asm("r5");
	register uint32_t eip asm("r6");
	register uint32_t opc asm("r7");
	register uint32_t op1 asm("r8");
	register uint32_t op2 asm("r9");

	env = (uint32_t)cpu;
	memory = (uint32_t)phys_ram_base;
	eip = cpu->eip;
	opc = saved_opc;
	op1 = saved_op1;
	op2 = saved_op2;

	asm volatile ("mov lr, pc\n"
		"mov pc, %0\n"
		: /* no outputs */
		: "r" (code)
		: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r14", "cc");

	cpu->eip = eip;
	saved_opc = opc;
	saved_op1 = op1;
	saved_op2 = op2;

	if (debug_stop) {
		printf("---------------cpu_env:\n");
		cpu_dump_state(cpu, stdout, fprintf);
		printf("opc: %s, op1: %08x, op2: %08x\n",
			cc_op_str[saved_opc], saved_op1, saved_op2);
	}
#endif
}

static void cpu_exec(CPUX86State *cpu)
{
	TranslationBlock *tb;
	uint32_t code_cnt;
	uint32_t source_pc;

	if ((cpu->segs[R_CS].flags & DESC_P_MASK) && (cpu->cr[0] & 1)) {
		source_pc = cpu->eip;
	} else {
		source_pc = (cpu->segs[R_CS].selector << 4) + cpu->eip;
	}

	tb = tb_find(source_pc);
	if (tb == NULL) {
		tb = tb_alloc(source_pc);
		if (tb == NULL) {
			tb_flush();
			tb = tb_alloc(source_pc);
		}

		code_cnt = gen_code(cpu, source_pc, tb->code_buf);
		tb->count = code_cnt;
		tb_set_offset((code_cnt + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
#if 1
		if (debug_stop) {
			printf("---------------translate arm(count: %d):\n", code_cnt);
			disas_arm(stdout, tb->code_buf, code_cnt * 4);
		}
#endif
	}

	run_arm_code(cpu, tb->code_buf);
}

void emu_start(void)
{

	CPUX86State *cpu_env;

	tb_init();
	cpu_env = cpu_init();
	while (1) {
		cpu_exec(cpu_env);
#if 0
		if ((cpu_env->segs[R_CS].selector == 0xf000) && (cpu_env->eip == 0x006b)) {
			debug_stop = 1;
		}
#endif
#if 0
		if ((cpu_env->segs[R_CS].selector == 0xf000) && (cpu_env->eip == 0xe2b0)) {
#if 0
{
		int i = 0, j = 0;
		for (i = 0; i < 2000; i++) {
			if ((j % 80) == 0)
				printf("\n");
			j++;
			printf("%c", *(char *)(phys_ram_base + 0xb8000 + 2 * i));
		}
		printf("\n");
}
#endif
			exit(0);
		}
#endif
		if (debug_stop) {
#if 0
			printf("%04x\n", *(uint16_t *)(phys_ram_base + 0xff96));
			printf("%04x\n", *(uint16_t *)(phys_ram_base + 0xff94));
			printf("%04x\n", *(uint16_t *)(phys_ram_base + 0xff92));
#endif
//			exit(0);
			getchar();
		}
		if ((cpu_env->segs[R_CS].selector == 0x0000) && (cpu_env->eip == 0x7c00)) {
#if 1
			int i = 0, j = 0;
			for (i = 0; i < 2000; i++) {
				if ((j % 80) == 0)
					printf("\n");
				j++;
				printf("%c", *(char *)(phys_ram_base + 0xb8000 + 2 * i));
			}
			printf("\n");
#endif
			printf("bios run OK\n");
			break;
		}
	}

	tb_exit();
	cpu_exit(cpu_env);
}

